/*
* Copyright (C) 2006-2016 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
package de.rcenvironment.core.configuration.internal;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.io.FileUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.jackson.JsonEncoding;
import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.JsonToken;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.type.TypeReference;
import org.osgi.framework.BundleContext;
import de.rcenvironment.core.configuration.ConfigurationService;
import de.rcenvironment.core.configuration.ConfigurationService.ConfigurablePathId;
import de.rcenvironment.core.configuration.PersistentSettingsService;
import de.rcenvironment.core.utils.common.JsonUtils;
/**
* Implementation of simple key-value store to persist settings of an RCE platform.
*
* @author Sascha Zur
* @author Robert Mischke
*/
public class PersistentSettingsServiceImpl implements PersistentSettingsService {
private static final String STORAGE_FILENAME = "settings.json";
private static final String ERROR_MSG_SAVE = "Could not save persistent settings: ";
private static final String ERROR_MSG_LOAD = "Could not find persistent settings file. It will be created: ";
private static final Log LOGGER = LogFactory.getLog(PersistentSettingsServiceImpl.class);
private ConfigurationService configurationService;
private File storageDirectory;
private Map<String, String> store;
private Set<String> alreadyBackupedFiles = new HashSet<>();
@Override
public synchronized void saveStringValue(String key, String value) {
saveStringValue(key, value, STORAGE_FILENAME);
}
private void saveStore(String filename) {
saveAnyMapInGivenStore(store, filename);
}
@Override
public synchronized String readStringValue(String key) {
return readStringValue(key, STORAGE_FILENAME);
}
private Map<String, String> readStore(String filename) {
Map<String, String> result = new HashMap<String, String>();
File storeFile = new File(storageDirectory, filename);
if (!storeFile.exists()) {
try {
storeFile.createNewFile();
} catch (IOException e) {
LOGGER.warn("Could not create new persistent settings file: ", e);
}
}
JsonFactory f = new JsonFactory();
JsonParser jp;
try {
jp = f.createJsonParser(new File(storageDirectory, filename));
jp.nextToken();
while (jp.hasCurrentToken() && jp.nextToken() != JsonToken.END_OBJECT) {
String fieldname = jp.getCurrentName();
if (jp.nextToken() == JsonToken.END_OBJECT) {
break;
} else {
result.put(fieldname, jp.getText());
}
}
jp.close();
} catch (IOException e) {
result = null;
// TODO review this error case
LOGGER.warn(ERROR_MSG_LOAD + "(This is normal if RCE is starting for the first time) :", e);
}
return result;
}
@Override
public synchronized void saveStringValue(String key, String value, String filename) {
if (store != null) {
store.put(key, value);
} else {
store = readStore(filename);
if (store == null) {
store = new HashMap<String, String>();
}
store.put(key, value);
}
saveStore(filename);
}
@Override
public synchronized String readStringValue(String key, String filename) {
store = readStore(filename);
if (store != null && store.containsKey(key)) {
return store.get(key);
}
return null;
}
@Override
public synchronized void delete(String key) {
delete(key, STORAGE_FILENAME);
}
@Override
public synchronized void delete(String key, String filename) {
store = readStore(filename);
if (store != null) {
store.remove(key);
}
saveStore(filename);
}
protected void activate(BundleContext context) {
if (storageDirectory == null) {
// TODO use File object instead of String where possible
storageDirectory = configurationService.getConfigurablePath(ConfigurablePathId.PROFILE_INTERNAL_DATA);
if (!storageDirectory.exists()) {
throw new RuntimeException("Unexpected state: Persistent settings path should exist at this point: "
+ storageDirectory);
}
}
alreadyBackupedFiles = new HashSet<>();
}
@Override
public synchronized Map<String, List<String>> readMapWithStringList(String filename) {
ObjectMapper mapper = JsonUtils.getDefaultObjectMapper();
File f = null;
f = new File(storageDirectory, filename);
if (!f.exists()) {
f.getParentFile().mkdirs();
}
Map<String, List<String>> result = new HashMap<String, List<String>>();
if (f.exists()) {
try {
result = mapper.readValue(f, new TypeReference<Map<String, List<String>>>() {
});
} catch (IOException e) {
LOGGER.error(ERROR_MSG_SAVE, e);
}
}
return result;
}
@Override
public synchronized void saveMapWithStringList(Map<String, List<String>> map, String filename) {
saveAnyMapInGivenStore(map, filename);
}
protected void bindConfigurationService(ConfigurationService newConfigurationService) {
configurationService = newConfigurationService;
}
// for unit testing
protected void setStorageDirectory(String path) {
this.storageDirectory = new File(path);
}
private void saveAnyMapInGivenStore(@SuppressWarnings("rawtypes") Map content, String filenameOfStore) {
File f = new File(storageDirectory, filenameOfStore);
if (!f.exists()) {
f.getParentFile().mkdirs();
}
if (f.exists()) {
try {
File backupFile = new File(storageDirectory, filenameOfStore + ".bak");
if (!alreadyBackupedFiles.contains(f.getAbsolutePath())) {
FileUtils.copyFile(f, backupFile);
alreadyBackupedFiles.add(f.getAbsolutePath());
}
} catch (IOException e) {
LOGGER.warn("PersistentSettingsService: Could not rename storage file to backup file", e);
}
}
// write JSON to a file
try {
JsonGenerator jsonGenerator = new JsonFactory().createJsonGenerator(f, JsonEncoding.UTF8);
JsonUtils.getDefaultObjectMapper().writeValue(jsonGenerator, content);
} catch (IOException e) {
// TODO >6.0.0: this should probably do more than just log a message
LOGGER.error(ERROR_MSG_SAVE, e);
}
}
}